package com.w67clement.mineapi.system.event;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.w67clement.mineapi.MineAPI;
import com.w67clement.mineapi.api.event.PacketCancellable;
import com.w67clement.mineapi.api.wrappers.MC_PacketWrapper;
import com.w67clement.mineapi.enums.PacketList;
import com.w67clement.mineapi.nms.NmsPacket;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import static com.w67clement.mineapi.MineAPI.*;
import static com.w67clement.mineapi.api.ReflectionAPI.*;
public class INCHandler implements IHandler
{
private static MineAPI mineapi;
private static Class<?> entityPlayer = getNmsClass("EntityPlayer");
private static Class<?> playerConnection = getNmsClass("PlayerConnection");
private static Field connection = getField(entityPlayer, "playerConnection", true);
private static Field network = getField(playerConnection, "networkManager", true);
private ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10, new ThreadFactoryBuilder().setNameFormat("MineAPI INCHandler #%d").build());
public INCHandler(MineAPI mineapi)
{
INCHandler.mineapi = mineapi;
}
private static Channel getChannel(Object networkManager)
{
return (Channel) getValue(networkManager, getFirstFieldOfType(networkManager.getClass(), Channel.class));
}
@Override
public void addChannel(final Player player)
{
final Object handle = NmsClass.getEntityPlayerByPlayer(player);
try
{
final Object connection = INCHandler.connection.get(handle);
final Channel channel = getChannel(network.get(connection));
this.threadPool.execute(() -> {
try
{
ChannelHandler handler = new ChannelHandler(player);
channel.pipeline().addBefore("packet_handler", "MineAPI", handler);
sendMessageToConsole(DEBUG_PREFIX + handler.getClass().getName() + " added to " + player.getName() + ".", true);
}
catch (Exception ignored)
{
}
});
}
catch (IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
}
@Override
public void removeChannel(Player player)
{
final Object handle = NmsClass.getEntityPlayerByPlayer(player);
try
{
final Object connection = INCHandler.connection.get(handle);
final Channel channel = getChannel(network.get(connection));
this.threadPool.execute(() -> {
try
{
try
{
io.netty.channel.ChannelHandler handler = channel.pipeline().get("MineAPI");
channel.pipeline().remove("MineAPI");
sendMessageToConsole(DEBUG_PREFIX + handler.getClass().getName() + " removed to " + player.getName() + ".", true);
}
catch (Exception ignored)
{
}
}
catch (Exception ignored)
{
}
});
}
catch (IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
}
@Override
public void addServerConnectionChannel()
{
Server server = Bukkit.getServer();
Object mc_Server = invokeMethod(server, getMethod(server, "getServer"));
Class<?> serverConnectionClass = getNmsClass("ServerConnection");
Object serverConnection = getValue(mc_Server, getFirstFieldOfType(getNmsClass("MinecraftServer"), serverConnectionClass));
Field field = getLastFieldOfType(serverConnectionClass, List.class);
List<?> currentList = (List<?>) getValue(serverConnection, field);
Field field1 = null;
if (currentList != null)
{
field1 = getField(currentList.getClass().getSuperclass(), "list", true);
}
Object obj = getValue(currentList, field1);
if (obj != null && obj.getClass().equals(PacketPingListenerList.class))
return;
List newList = Collections.synchronizedList(new PacketPingListenerList());
if (currentList != null)
{
newList.addAll(currentList.stream().collect(Collectors.toList()));
}
setValue(serverConnection, field, newList);
}
private void removeServerConnectionChannel()
{
Server server = Bukkit.getServer();
Object mc_Server = invokeMethod(server, getMethod(server, "getServer"));
Class<?> serverConnectionClass = getNmsClass("ServerConnection");
Object serverConnection = getValue(mc_Server, getFirstFieldOfType(getNmsClass("MinecraftServer"), serverConnectionClass));
Field field = getLastFieldOfType(serverConnectionClass, List.class);
List<?> currentList = (List<?>) getValue(serverConnection, field);
Field field1 = null;
if (currentList != null)
{
field1 = getField(currentList.getClass().getSuperclass(), "list", true);
}
Object obj = getValue(currentList, field1);
if (obj != null && obj.getClass().equals(PacketPingListenerList.class))
{
List newList = Collections.synchronizedList(new ArrayList<>());
if (currentList != null)
{
newList.addAll(currentList.stream().collect(Collectors.toList()));
}
setValue(serverConnection, field, newList);
}
}
@Override
public void disable()
{
this.removeServerConnectionChannel();
}
private class PacketPingListenerList<E> extends ArrayList<E>
{
private ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(15, new ThreadFactoryBuilder().setNameFormat("MineAPI Ping Listener #%d").build());
@Override
public boolean add(E paramE)
{
final E a = paramE;
this.threadPool.execute(() -> {
try
{
Channel channel = null;
while (channel == null)
{
channel = getChannel(a);
}
channel.pipeline().addBefore("packet_handler", "MineAPI_Ping", new INCChannelHandler());
}
catch (Exception ignored)
{
}
});
return super.add(paramE);
}
@Override
public boolean remove(Object paramE)
{
final Object a = paramE;
this.threadPool.execute(() -> {
try
{
Channel channel = null;
while (channel == null)
{
channel = getChannel(a);
}
channel.pipeline().remove("MineAPI_Ping");
}
catch (Exception ignored)
{
}
});
return super.remove(paramE);
}
}
public class ChannelHandler extends ChannelDuplexHandler
{
private Player player;
public ChannelHandler(Player p)
{
this.player = p;
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception
{
PacketCancellable cancel = new PacketCancellable();
NmsPacket mineapiPacket = null;
try
{
PacketList packet = PacketList.getPacketByName(msg.getClass().getSimpleName());
assert packet != null : "Error: [{\"class\":\"INCHandler$ChannelHandler\",\"method\":\"write(ChannelHandlerContext, Object, ChannelPromise)\",\"line\":230,\"error\":\"Unknow packet '" + msg.getClass().getSimpleName() + "' in PacketList\"}], please contact author and report the bug.";
if (packet.hasMineAPIPacket())
if (getNmsManager().hasPacketDecoder(packet.getMineAPIPacket()))
{
mineapiPacket = getNmsManager().getPacketDecoder(packet.getMineAPIPacket()).decode(msg);
}
else
{
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Can't find the decoder for the packet '" + packet.getPacketName() + "'.");
}
}
catch (NullPointerException ignored)
{
}
catch (RuntimeException e)
{
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Error detected in the INCHandler: " + e.getClass().getSimpleName(), true);
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Message: " + e.getMessage(), true);
if (debug)
e.printStackTrace();
}
catch (Exception ignored)
{
}
MC_PacketWrapper<?> packetWrapper = new MC_PacketWrapper<>(mineapiPacket, msg);
mineapi.packetSend(packetWrapper, cancel, player);
if (cancel.isCancelled())
{
return;
}
if (mineapiPacket == null)
{
if (!cancel.hasForceChanges())
{
super.write(ctx, msg, promise);
return;
}
}
super.write(ctx, packetWrapper.getNmsPacket(), promise);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
{
PacketCancellable cancel = new PacketCancellable();
NmsPacket mineapiPacket = null;
try
{
PacketList packet = PacketList.getPacketByName(msg.getClass().getSimpleName());
assert packet != null : "Error: [{\"class\":\"INCHandler$ChannelHandler\",\"method\":\"channelRead(ChannelHandlerContext, Object)\",\"line\":279,\"error\":\"Unknow packet '" + msg.getClass().getSimpleName() + "' in PacketList\"}], please contact author and report the bug.";
if (packet.hasMineAPIPacket())
if (getNmsManager().hasPacketDecoder(packet.getMineAPIPacket()))
{
mineapiPacket = getNmsManager().getPacketDecoder(packet.getMineAPIPacket()).decode(msg);
}
else
{
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Can't find the decoder for the packet '" + packet.getPacketName() + "'.");
}
}
catch (NullPointerException ignored)
{
}
catch (RuntimeException e)
{
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Error detected in the INCHandler: " + e.getClass().getSimpleName(), true);
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Message: " + e.getMessage(), true);
if (debug)
e.printStackTrace();
}
catch (Exception ignored)
{
}
MC_PacketWrapper<?> packetWrapper = new MC_PacketWrapper<>(mineapiPacket, msg);
mineapi.packetReceive(packetWrapper, cancel, player);
if (cancel.isCancelled())
{
return;
}
if (mineapiPacket == null)
{
if (!cancel.hasForceChanges())
{
super.channelRead(ctx, msg);
return;
}
}
super.channelRead(ctx, packetWrapper.getNmsPacket());
}
@Override
public String toString()
{
return "ChannelHandler (" + this.player + ")";
}
}
public class INCChannelHandler extends ChannelDuplexHandler
{
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception
{
if (!msg.getClass().getSimpleName().startsWith("PacketStatus"))
{
super.write(ctx, msg, promise);
return;
}
PacketCancellable cancel = new PacketCancellable();
NmsPacket<?> mineapiPacket = null;
try
{
PacketList packet = PacketList.getPacketByName(msg.getClass().getSimpleName());
assert packet != null : "Error: [{\"class\":\"INCHandler$INCChannelHandler\",\"method\":\"write(ChannelHandlerContext, Object, ChannelPromise)\",\"line\":343,\"error\":\"Unknow packet '" + msg.getClass().getSimpleName() + "' in PacketList\"}], please contact author and report the bug.";
if (packet.hasMineAPIPacket())
if (getNmsManager().hasPacketDecoder(packet.getMineAPIPacket()))
{
mineapiPacket = getNmsManager().getPacketDecoder(packet.getMineAPIPacket()).decode(msg);
}
else
{
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Can't find the decoder for the packet '" + packet.getPacketName() + "'.");
}
}
catch (NullPointerException ignored)
{
}
catch (RuntimeException e)
{
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Error detected in the INCHandler: " + e.getClass().getSimpleName(), true);
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Message: " + e.getMessage(), true);
if (debug)
e.printStackTrace();
}
catch (Exception ignored)
{
}
MC_PacketWrapper<?> packetWrapper = new MC_PacketWrapper(mineapiPacket, msg);
mineapi.pingPacketSend(packetWrapper, cancel, ctx.channel().remoteAddress().toString());
if (cancel.isCancelled())
{
return;
}
if (mineapiPacket == null)
{
if (!cancel.hasForceChanges())
{
super.write(ctx, msg, promise);
return;
}
}
super.write(ctx, packetWrapper.getNmsPacket(), promise);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
{
if (msg.getClass().getSimpleName().equals("PacketHandshakingInSetProtocol") && hasEnableDebugConnection())
{
sendMessageToConsole(CONNECTION_PREFIX + "[" + ctx.channel().remoteAddress().toString() + "] " + this.getClass().getSimpleName() + " has connected.");
}
if ((!msg.getClass().getSimpleName().startsWith("PacketStatus")) || (!msg.getClass().getSimpleName().equals("PacketHandshakingInSetProtocol")))
{
super.channelRead(ctx, msg);
return;
}
PacketCancellable cancel = new PacketCancellable();
NmsPacket mineapiPacket = null;
try
{
PacketList packet = PacketList.getPacketByName(msg.getClass().getSimpleName());
assert packet != null : "Error: [{\"class\":\"INCHandler$INCChannelHandler\",\"method\":\"channelRead(ChannelHandlerContext, Object)\",\"line\":401,\"error\":\"Unknow packet '" + msg.getClass().getSimpleName() + "' in PacketList\"}], please contact author and report the bug.";
if (packet.hasMineAPIPacket())
if (getNmsManager().hasPacketDecoder(packet.getMineAPIPacket()))
{
mineapiPacket = getNmsManager().getPacketDecoder(packet.getMineAPIPacket()).decode(msg);
}
else
{
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Can't find the decoder for the packet '" + packet.getPacketName() + "'.");
}
}
catch (NullPointerException ignored)
{
}
catch (RuntimeException e)
{
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Error detected in the INCHandler: " + e.getClass().getSimpleName(), true);
sendMessageToConsole(DEBUG_PREFIX + ChatColor.DARK_RED + "[ERROR]" + ChatColor.RED + " Message: " + e.getMessage(), true);
if (debug)
e.printStackTrace();
}
catch (Exception ignored)
{
}
MC_PacketWrapper<?> packetWrapper = new MC_PacketWrapper<>(mineapiPacket, msg);
mineapi.pingPacketReceive(packetWrapper, cancel, ctx.channel().remoteAddress().toString());
if (cancel.isCancelled())
{
return;
}
if (mineapiPacket == null)
{
if (!cancel.hasForceChanges())
{
super.channelRead(ctx, msg);
return;
}
}
super.channelRead(ctx, packetWrapper.getNmsPacket());
}
}
}